﻿using System;
using System.ComponentModel;
using Brjnk.Common.ValueTypes;

namespace Brjnk.Common
{
    [TypeConverter(typeof(StringValueConverter<Azimuth>))]
    public struct Azimuth : IPhysicalQuantity
    {
        //azimuth in degrees
        private double azimuth;

        /// <summary>
        /// Azimuth from first point to second point
        /// </summary>
        public Azimuth(GpsCoordinates Coordinate1, GpsCoordinates Coordinate2)
        {
            azimuth = GetAzimuth(Coordinate1, Coordinate2);
        }

        public Azimuth(double degrees)
        {
            this.azimuth = degrees;
        }

        /// <summary>
        /// Return azimuth in degrees as string
        /// </summary>
        /// <param name="DecimalPlaceCount">Count of decimal places</param>
        public string ToString(int DecimalPlaceCount = 2)
        {
            return string.Format("{0:F" + DecimalPlaceCount + "}°", azimuth);
        }

        /// <summary>
        /// Return azimuth value in degrees
        /// </summary>
        /// <value></value>
        public double ValueDeg
        {
            get { return azimuth; }
        }

        public double ValueRad
        {
            get { return MathEx.ToRadians(azimuth); }
        }

        /// <summary>
        /// Return azimuth from first point to second point in degrees
        /// </summary>
        /// <param name="Coordinate1">First point</param>
        /// <param name="Coordinate2">Second point</param>
        /// <returns>Azimuth in degrees</returns>
        /// <remarks></remarks>
        public static double GetAzimuth(GpsCoordinates Coordinate1, GpsCoordinates Coordinate2)
        {
            double lat1 = Coordinate1.LatTotalDegree;
            double lat2 = Coordinate2.LatTotalDegree;
            double lon1 = Coordinate1.LonTotalDegree;
            double lon2 = Coordinate2.LonTotalDegree;

            //convert to radians
            lat1 = lat1 * (Math.PI / 180);
            lat2 = lat2 * (Math.PI / 180);
            lon1 = lon1 * (Math.PI / 180);
            lon2 = lon2 * (Math.PI / 180);

            double a = Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos((lon2) - (lon1));
            double b = Math.Sin(lon2 - lon1) * Math.Cos(lat2);
            double deg = ToDegrees(Math.Atan2(b, a) % (2 * Math.PI));
            if (deg < 0) return deg + 360;
            else return deg;
        }

        /// <summary>
        /// Implicitní konverze vrací azimut ve stupních
        /// </summary>
        public static implicit operator double(Azimuth azimuth)
        {
            return azimuth;
        }

        public static implicit operator Azimuth(double a)
        {
            return new Azimuth(a);
        }

        private static double ToDegrees(double radians)
        {
            return (radians * 180) / Math.PI;
        }

        public override string ToString()
        {
            return string.Format("{0:f02} °", azimuth);
        }

        static Azimuth()
        {
            Parser = new ValueParser();
            Parser.AddUnit("°", 1);
        }

        public static readonly ValueParser Parser;

        public double GetValue()
        {
            return ValueRad;
        }

        public double GetCustomUnitValue()
        {
            return azimuth;
        }

        public string GetCustomUnitName()
        {
            return "°";
        }

        public static string GetUnitName()
        {
            return "°";
        }
    }
}